fix(web): integrate compiled game WASM into the web build (#72)#73
Merged
Conversation
The web build flow (`./native/web/build.sh game.ts`) never produced a working game: `build.sh` compiled the game to a throwaway `/tmp/bloom_game.html` and copied an unrelated template, so the game WASM was never loaded — `bloom_glue.js` only warned. The FFI bridge was also wrong: it manually NaN-box-converted every argument, but Perry's runtime already wraps the `ffi` namespace (`wrapFfiForI64`), delivering plain JS values and re-encoding returns, so the manual conversion double-converted. Reuse Perry's self-contained HTML as the carrier of its own correct `rt` runtime + NaN-boxing, and splice the Bloom engine into it: - bloom_glue.js: rewritten as the engine bootstrap + FFI bridge following Perry's plain-value contract. Maps bloom_* straight through, routes string/asset params to the _str/_bytes variants, wires input/audio/HiDPI canvas, and drives the rAF game loop via Perry's global callWasmClosure (runGame short-circuits to bloom_run_game on web, platform === 7). - splice_game.py (new): injects the canvas + a synchronously-created window.__bloomReady promise + the deferred bootstrap module into Perry's HTML, and gates the game's bootPerryWasm() on that promise so the game boots only after the engine + FFI are live. Anchored to the boot block so it ignores decoy `.catch(` in the runtime. - index.html: now a thin engine-only page (no-game case). - build.sh: resolve the game path up-front (was tested from the wrong cwd after cd'ing into the web crate, silently skipping compilation), compile to a temp dir, splice into dist/web/index.html, clean up. - CLAUDE.md: updated file roles + the FFI value contract. Validated end-to-end against Perry v0.5.1206 + a full wasm-pack engine build of examples/pong: produces dist/web/index.html with Perry's runtime, the embedded game WASM, the gated boot, and the Bloom canvas.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #72.
Problem
./native/web/build.sh game.tsnever produced a working game:build.shcompiled the game to a throwaway/tmp/bloom_game.htmland copied an unrelated template, so the game WASM was never loaded —bloom_glue.jsonlyconsole.warn'd.ffinamespace (wrapFfiForI64), delivering plain JS values and re-encoding returns. The manual conversion would have crashed.build.shbug: itcd'd into the web crate, then tested the relative game path from the wrong cwd, silently skipping compilation.Approach
Reimplementing Perry's ~280-function
rtruntime in JS (the issue's suggested approach #1) is infeasible. Instead, reuse Perry's self-contained HTML as the carrier of its own correct runtime + NaN-boxing, and splice the Bloom engine into it.Changes
bloom_glue.js— rewritten as the engine bootstrap + FFI bridge following Perry's plain-value contract. Mapsbloom_*straight through, routes string/asset params to the_str/_bytesvariants, wires input/audio/HiDPI canvas, and drives the rAF game loop via Perry's globalcallWasmClosure(runGameshort-circuits tobloom_run_gameon web,platform === 7).splice_game.py(new) — injects the Bloom canvas + a synchronously-createdwindow.__bloomReadypromise + the deferred bootstrap module into Perry's HTML, and gates the game'sbootPerryWasm()call on that promise so the game boots only after the engine + FFI are live. Anchored to the boot block so it ignores decoy.catch(in the runtime.index.html— now a thin engine-only page (no-game case).build.sh— resolves the game path up-front (bug fix), compiles to a temp dir, splices intodist/web/index.html, cleans up.CLAUDE.md— updated file roles + the FFI value contract.Validation
Verified end-to-end against the real Perry compiler (v0.5.1206) and a full
wasm-packengine build ofexamples/pong: the pipeline producesdist/web/index.htmlcontaining Perry's runtime, the embedded game WASM, the gated boot, and the Bloom canvas; everypkgexport the bridge references exists; the splicer correctly ignores decoy patterns; temp files are cleaned up; all new JS/Python passes syntax checks.Browser-runtime behavior (WebGPU surface creation, live closure dispatch) was not exercised headless —
cd dist/web && python3 -m http.server 8080would confirm the last mile. Note the build needsPERRY_ALLOW_PERRY_FEATURES=1(orbloom/*in the hostperry.allow.nativeLibrary) since Bloom links native libraries.